#include "BufferManager.h"
#include "BTNode.h"

BufferManager::BufferManager(int mId, int mSize, int psize, int w, int s, int bt)
{
	maxId = mId;
	maxSize = mSize;
	pageSize = psize;
	currentSize = currentId = readIO = writeIO = updateTime = totalupdate = 0;
	buffertype = bt;

	entryArray = new BufEntry[maxId];
	activeEntry = new int[maxSize + 1];

	for(int i = 0; i < maxId; i++)
	{
		entryArray[i].nodeId = -1;
		entryArray[i].last_time = -1;
		entryArray[i].dirtyBit = false;
		entryArray[i].memBit = false;
	}
	ptable = new PageTable(pageSize);
	his = new Histogram(w, s);

	cout << "Buffer manager created: maxSize:" << maxSize << endl;

	diskfile.open("disk.data", ios::out);

	char* buffer = new char[pageSize];

	for(int i = 0; i < maxId; i++)
	{
		diskfile.write(buffer, pageSize);
	}

	diskfile.close();

	diskfile.open("disk.dat", ios::in | ios::out | ios::binary);

	delete [] buffer;
}

BufferManager::~BufferManager()
{
	int i = 0;
	for(; i < maxId; i++)
	{
		if(entryArray[i].nodeId != -1)
		{
			delete (Page*)(entryArray[i].pointer);
		}
	}

	delete [] entryArray;
	diskfile.close();
}

int BufferManager::RequestID()
{
	if(currentId == maxId)
	{
		cout << "Max Page ID extends to :" << maxId * 2 << endl;
		int newMaxId = 2 * maxId;

		BufEntry * newEntryArray = new BufEntry[newMaxId];
		int i = 0;

		for(; i < maxId; i++)
		{
			newEntryArray[i].pointer = entryArray[i].pointer;
			newEntryArray[i].last_time = entryArray[i].last_time;
			newEntryArray[i].memBit = entryArray[i].memBit;
			newEntryArray[i].nodeId = entryArray[i].nodeId;
			newEntryArray[i].dirtyBit = entryArray[i].dirtyBit;
		}

		delete[] entryArray;
		entryArray = newEntryArray;
		maxId = newMaxId;
	}

	currentId++;

	return currentId - 1;
}

bool BufferManager::AddEntry(void* pointer, int id)
{
	if(id < 0 || id >= maxId){
		cout << "Add Entry : Incorrect ID " <<  id << endl;
		return false;
	}

	entryArray[id].pointer = pointer;
	entryArray[id].nodeId = id;
	entryArray[id].memBit = true;
	entryArray[id].dirtyBit = true;

	totalupdate += updateTime - entryArray[id].last_time + 1;
	entryArray[id].last_time = updateTime++;

	activeEntry[currentSize] = id;

	currentSize++;

	if(currentSize > maxSize)
	{
		int saveEntry;
		if(buffertype == 0 || buffertype == 1)
			saveEntry = PickupSaveEntry(id);
		else if(buffertype == 2)
			saveEntry = NewPickupSaveEntry(id, TIMERATIO, RANGERATIO);
		
		BTNode * node = (BTNode*)(entryArray[saveEntry].pointer);

		if(!node->SaveToFile())
		{
			cout << "save error" << endl;
		}
	}

	return true;
}

bool BufferManager::CopyFromDisk(int id, char* buffer)
{
	if(entryArray[id].nodeId == -1)
		return false;

	//int pos = ptable->ConvertIdToAdd(id);

	//diskfile.seekg(pos, ios::beg);

	//diskfile.read(buffer, pageSize);
	readIO++;

	return true;
}

bool BufferManager::SaveToDisk(int id, char* buffer)
{
	if(entryArray[id].nodeId == -1)
		return false;

	//int pos = ptable->ConvertIdToAdd(id);

	//diskfile.seekp(pos, ios::beg);

	//diskfile.write(buffer, pageSize);
	writeIO++;

	return true;
}

int BufferManager::PickupSaveEntry(int curID)
{
	//cout << "One page needs to be evicted!" << endl;
	int minEntry = -1;
	long earlyTime;

	int i, cur = -1;

	for(i = 0; i < maxSize + 1; i++)
	{
		if(activeEntry[i] == curID)
			continue;

		if(entryArray[activeEntry[i]].nodeId == -1 || !entryArray[activeEntry[i]].memBit)
			continue;

		if(minEntry == -1)
		{
			minEntry = activeEntry[i];
			cur = i;
			earlyTime = entryArray[activeEntry[i]].last_time;
		}
		else if(entryArray[activeEntry[i]].last_time < earlyTime)
		{
			minEntry = activeEntry[i];
			cur = i;
			earlyTime = entryArray[activeEntry[i]].last_time;
		}
	}

	activeEntry[cur] = activeEntry[currentSize - 1];

	return minEntry;
}

int BufferManager::NewPickupSaveEntry(int curID, double timeratio, double rangeratio)
{
	int minEntry = -1, minEntryDirty = -1;
	double decision, decisisonDirty;

	int i, cur = -1, curDirty = -1;
	double time = 0.0, range = 0.0, temp = 0.0;

	for(i = 0; i < maxSize + 1; i++)
	{
		if(activeEntry[i] == curID)
			continue;

		if(entryArray[activeEntry[i]].nodeId == -1 || !entryArray[activeEntry[i]].memBit)
			continue;

		time = (double)entryArray[activeEntry[i]].last_time / (double)totalupdate;
		range = his->RangeRatio(((BTNode*)entryArray[activeEntry[i]].pointer)->leftFence, ((BTNode*)entryArray[activeEntry[i]].pointer)->rightFence);
		temp = time * timeratio + range * rangeratio;

		if(entryArray[activeEntry[i]].dirtyBit)
		{

			if(minEntryDirty == -1)
			{
				minEntryDirty = activeEntry[i];
				curDirty = i;
				decisisonDirty = temp;
			}
			else if(temp < decisisonDirty)
			{
				minEntryDirty = activeEntry[i];
				curDirty = i;
				decisisonDirty = temp;
			}

			continue;
		}

		if(minEntry == -1)
		{
			minEntry = activeEntry[i];
			cur = i;
			decision = temp;
		}
		else if(temp < decision)
		{
			minEntry = activeEntry[i];
			cur = i;
			decision = temp;
		}
	}

	if(minEntry == -1)
	{
		minEntry = minEntryDirty;
		cur = curDirty;
	}

	activeEntry[cur] = activeEntry[currentSize - 1];

	return minEntry;
}

void BufferManager::LoadUpdate(int id)
{
	if(id >= maxId)
	{
		cout << "Load : Incorrect ID " << id << endl;
		return;
	}

	totalupdate += updateTime - entryArray[id].last_time + 1;

	entryArray[id].last_time = updateTime++;
	entryArray[id].memBit = true;
	entryArray[id].dirtyBit = false;

	activeEntry[currentSize] = id;

	currentSize++;

	if(currentSize > maxSize)
	{
		int saveEntry;
		if(buffertype == 0 || buffertype == 1)
			saveEntry = PickupSaveEntry(id);
		else if(buffertype == 2)
			saveEntry = NewPickupSaveEntry(id, TIMERATIO, RANGERATIO);

		if(!((Page*)(entryArray[saveEntry].pointer))->SaveToFile())
			cout << "file saving error" << endl;
	}
}

void BufferManager::SaveUpdate(int id)
{
	if(id < 0 || id >= maxId)
	{
		cout << "Save : Incorrect ID " << id << endl;
		return;
	}

	entryArray[id].memBit = false;

	for(int i = 0; i < currentSize; i++)
	{
		if(activeEntry[i] == id)
		{
			activeEntry[i] = activeEntry[currentSize - 1];
			break;
		}
	}

	currentSize--;
	totalupdate -= entryArray[id].last_time;
}

void BufferManager::WriteUpdate(int id)
{
	if(id < 0 || id >= maxId)
	{
		cout << "Write : Incorrect ID " << id << endl;
		return;
	}

	totalupdate += updateTime - entryArray[id].last_time + 1;
	entryArray[id].last_time = updateTime++;
	entryArray[id].dirtyBit = true;

	if(buffertype == 0)
	{
		if(!((Page*)(entryArray[id].pointer))->SaveToFile())
			cout << "file saving error" << endl;
	}
}

void BufferManager::ReadUpdate(int id)
{
	if(id >= maxId)
	{
		cout << "Read : Incorrect ID " << id << endl;
		return;
	}

	totalupdate += updateTime - entryArray[id].last_time + 1;

	entryArray[id].last_time = updateTime++;
}

bool BufferManager::isDirty(int id)
{
	if(id < 0 || id >= maxId)
	{
		cout << "wrong id " << endl;
		return false;
	}

	return entryArray[id].dirtyBit;
}
